//
//  ADTimer.swift
//  ADKit
//
//  Created by Adoma on 2023/12/30.
//

import RxSwift

public class ADTimer {
    
    private let timerDisposable = SerialDisposable()
    private let scheduler = ConcurrentDispatchQueueScheduler(qos: .default)
    private let label: String
    
    public init(_ label: String = "") {
        self.label = label
    }
    
    public struct Time: Equatable {
        
        public let days: Int
        
        public let hour: Int
        public let hours: Int
        
        public let minute: Int
        public let minutes: Int
        
        public let second: Int
        public let seconds: Int
        
        public let isFinished: Bool
        
        init(seconds: Int, isFinished: Bool) {
            
            self.isFinished = isFinished
            
            let (minutes, second) = seconds.quotientAndRemainder(dividingBy: 60)
            self.seconds = seconds
            self.second = second
            
            let (hours, minute) = minutes.quotientAndRemainder(dividingBy: 60)
            self.minutes = minutes
            self.minute = minute
            
            let (days, hour) = hours.quotientAndRemainder(dividingBy: 24)
            self.hours = hours
            self.hour = hour
            
            self.days = days
        }
        
        public static func == (lhs: Self, rhs: Self) -> Bool {
            lhs.seconds == rhs.seconds && lhs.isFinished == rhs.isFinished
        }
    }
    
    /// 倒计时
    /// - Parameters:
    ///   - total: 倒计时总时长，秒
    ///   - dueTime: 从开始倒计时到第一次执行倒计时的间隔，秒
    ///   - increase: 是否返回为正计时，默认false
    ///   - handler: 回调是否超时和新的倒计时
    /// by ADKit
    public func countDown(seconds total: Int, dueTime:Int = 0, increase: Bool = false, handler: @escaping (Time)->Void) {
        
        let cancelable = SingleAssignmentDisposable()
        self.timerDisposable.disposable = cancelable
        
        let source = Observable<Int>.timer(.seconds(dueTime), period: .seconds(1), scheduler: scheduler)
            .map{
                let seconds = increase ? $0 : total - $0
                let isFinished = $0 == total
                return Time(seconds: seconds, isFinished: isFinished)
            }
            .take(until: { $0.isFinished }, behavior: .inclusive)
            .observe(on: MainScheduler.asyncInstance)
            .subscribe{ time in
                handler(time)
            } onDisposed: {
                ADLogger(adkit: Self.self)
                    .debug("countDown onDisposed")
            }
        
        cancelable.setDisposable(source)
    }
    
    /// by ADKit
    public func countDown(to date: Date, dueTime:Int = 0, increase: Bool = false, handler: @escaping (Time)->Void) {
        
        let cancelable = SingleAssignmentDisposable()
        self.timerDisposable.disposable = cancelable
        
        let source = Observable<Int>.timer(.milliseconds(dueTime), period: .milliseconds(1), scheduler: scheduler)
            .map{
                let interval = Int(ceil(date.timeIntervalSinceNow))
                let seconds = increase ? Int(ceil(Double($0)/1000)) : interval
                let isFinished = interval <= 0
                return Time(seconds: seconds, isFinished: isFinished)
            }
            .take(until: { $0.isFinished }, behavior: .inclusive)
            .distinctUntilChanged()
            .observe(on: MainScheduler.asyncInstance)
            .subscribe{ time in
                handler(time)
            } onDisposed: {
                ADLogger(adkit: Self.self)
                    .debug("countDown onDisposed")
            }
        
        cancelable.setDisposable(source)
    }
    
    /// 延迟执行，一次性定时器
    /// by ADKit
    public func delay(seconds: Int, handler: @escaping ()->Void) {
        
        let cancelable = SingleAssignmentDisposable()
        self.timerDisposable.disposable = cancelable
        
        let source = Observable<Int>.timer(.seconds(seconds), scheduler: scheduler)
            .observe(on: MainScheduler.asyncInstance)
            .subscribe{ _ in
                handler()
            } onDisposed: {
                ADLogger(adkit: Self.self)
                    .debug("delay onDisposed")
            }
        
        cancelable.setDisposable(source)
    }
    
    /// 间隔执行
    /// by ADKit
    public func interval(seconds: Int, handler: @escaping (Int)->Void) {
        
        let cancelable = SingleAssignmentDisposable()
        self.timerDisposable.disposable = cancelable
        
        let source = Observable<Int>.interval(.seconds(seconds), scheduler: scheduler)
            .observe(on: MainScheduler.asyncInstance)
            .subscribe{ time in
                handler(time)
            } onDisposed: {
                ADLogger(adkit: Self.self)
                    .debug("interval onDisposed")
            }
        
        cancelable.setDisposable(source)
    }
    
    /// by ADKit
    public func invalidate() {
        ADLogger(adkit: Self.self).debug(label, #function)
        timerDisposable.disposable = Disposables.create()
    }
    
    deinit {
        ADLogger(adkit: Self.self).debug(label, #function)
        timerDisposable.dispose()
    }
}
